package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.SupplyChannel;
import com.lkd.contract.SupplyContract;
import com.lkd.contract.TaskCompleteContract;
import com.lkd.dao.TaskDao;
import com.lkd.dao.TaskTypeDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.TaskDetailsEntity;
import com.lkd.entity.TaskEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.UserService;
import com.lkd.feign.VMService;
import com.lkd.http.view.BaseResponse;
import com.lkd.http.vo.CancelTaskViewModel;
import com.lkd.http.vo.TaskDetailsViewModel;
import com.lkd.http.vo.TaskViewModel;
import com.lkd.service.TaskDetailsService;
import com.lkd.service.TaskService;
import com.lkd.utils.JsonUtil;
import com.lkd.vo.Pager;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TaskServiceImpl extends ServiceImpl<TaskDao,TaskEntity> implements TaskService{

    @Autowired
    private UserService userService;

    @Autowired
    private VMService vmService;

    @Autowired
    private TaskDetailsService taskDetailsService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MqttProducer mqttProducer;

    @Override
    public Pager<TaskEntity> search(Long pageIndex, Long pageSize, String innerCode, Integer userId, String taskCode, Integer status, Boolean isRepair, String start, String end) {
        Page<TaskEntity> page = new Page<>(pageIndex,pageSize);
        LambdaQueryWrapper<TaskEntity> qw = new LambdaQueryWrapper<>();
        if(!Strings.isNullOrEmpty(innerCode)){
            qw.eq(TaskEntity::getInnerCode,innerCode);
        }
        if(userId != null && userId > 0){
            qw.eq(TaskEntity::getUserId,userId);
        }
        if(!Strings.isNullOrEmpty(taskCode)){
            qw.like(TaskEntity::getTaskCode,taskCode);
        }
        if(status != null && status > 0){
            qw.eq(TaskEntity::getTaskStatus,status);
        }
        if(isRepair != null){
            if(isRepair){
                qw.ne(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
            }else {
                qw.eq(TaskEntity::getProductTypeId,VMSystem.TASK_TYPE_SUPPLY);
            }
        }
        if(!Strings.isNullOrEmpty(start) && !Strings.isNullOrEmpty(end)){
            qw
                    .ge(TaskEntity::getCreateTime,LocalDate.parse(start,DateTimeFormatter.ISO_LOCAL_DATE))
                    .le(TaskEntity::getCreateTime,LocalDate.parse(end,DateTimeFormatter.ISO_LOCAL_DATE));
        }
        //根据最后更新时间倒序排序
        qw.orderByDesc(TaskEntity::getUpdateTime);

        return Pager.build(this.page(page,qw));
    }

    /**
     * 新建运维工单 运营工单
     *
     * @param model
     * @return
     */
    @Override
    public boolean createTask(TaskViewModel model) {
        //判断该设备有无未完成工单
        if (havaTask(model)) {
            throw new LogicException("还设备有未完成工单");
        }
        //根据设备编号 查询设备
        VmVO vmInfo = vmService.getVMInfo(model.getInnerCode());
        //设备状态判断
        check(model ,vmInfo);
        //拷贝数据
        TaskEntity taskEntity = setTaskEntity(model, vmInfo);
        //补货工单
        replenish(model, taskEntity);
        //新增工单
        boolean save = save(taskEntity);

        //创建工单后redis中执行人的工单数加1
        updateUserTaskRedis(taskEntity , 1);
        return save;
    }

    /**
     * 创建或取消工单后 修改redis中人员的工单数量
     * @param taskEntity
     * @param score
     */
    private void updateUserTaskRedis(TaskEntity taskEntity, int score) {

        //假设工单是运维工单 , 则执行人是运维人员
        String roleCode = VMSystem.USER_REPAIRER;
        //如果该工单是补货工单
        if (taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_SUPPLY) {
            //则执行人是运营人员
             roleCode = VMSystem.USER_OPERATOR;
        }
        //以固定字符串（前缀）+日期+区域+工单类别（运营/运维）为大key，以人员id做小key，过期时间为2天。
        String key = VMSystem.REGION_TASK_KEY_PREF
                + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))
                + taskEntity.getRegionId()
                + roleCode;

        redisTemplate.opsForZSet().incrementScore(key ,taskEntity.getUserId() ,  score);


    }


    /**
     * 判断该设备有无未完成工单
     * @param model
     */
    private boolean havaTask(TaskViewModel model) {
        int count = count(Wrappers.<TaskEntity>lambdaQuery().select(TaskEntity::getTaskId)
                .eq(TaskEntity::getInnerCode, model.getInnerCode())
                .eq(TaskEntity::getProductTypeId, model.getProductType())
                .le(TaskEntity::getTaskStatus, VMSystem.TASK_STATUS_PROGRESS));
        return count > 0;
    }

    /**
     * 补货工单
     * @param model
     * @param taskEntity
     */
    private void replenish(TaskViewModel model, TaskEntity taskEntity) {
        if (model.getProductType() == VMSystem.TASK_TYPE_SUPPLY){
            List<TaskDetailsViewModel> details = model.getDetails();
            for (TaskDetailsViewModel detail : details) {
                TaskDetailsEntity taskDetailsEntity = new TaskDetailsEntity();
                BeanUtils.copyProperties(detail ,taskDetailsEntity );
                taskDetailsEntity.setTaskId(taskEntity.getTaskId());
                    taskDetailsService.save(taskDetailsEntity);
            }
        }
    }

    /**
     * 拷贝数据
     * @param model
     * @param vmInfo
     *
     */
    private TaskEntity setTaskEntity(TaskViewModel model, VmVO vmInfo) {
        TaskEntity taskEntity = new TaskEntity();
        taskEntity.setTaskCode(createTaskCode());
        taskEntity.setInnerCode(model.getInnerCode());
        taskEntity.setCreateType(model.getCreateType());
        taskEntity.setUserId(model.getUserId());

        taskEntity.setAssignorId(model.getAssignorId());

        taskEntity.setDesc(model.getDesc());
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_CREATE);
        taskEntity.setRegionId(vmInfo.getRegionId());
        taskEntity.setAddr(vmInfo.getNodeAddr());
        taskEntity.setProductTypeId(model.getProductType());
        //根据userId查询用户名称
        String userName = userService.getUser(model.getUserId()).getUserName();
        taskEntity.setUserName(userName);
        return taskEntity;
    }

    /**
     * 取消工单
     *
     * @param taskId 工单id
     * @param desc   拒绝理由
     * @return
     */
    @Override
    public boolean cancelTask(String taskId, CancelTaskViewModel desc) {
        TaskEntity task = getById(Long.parseLong(taskId));
        if (task.getTaskStatus() != VMSystem.TASK_STATUS_PROGRESS){
            throw new LogicException("工单不处于进行状态");
        }
        task.setTaskStatus( VMSystem.TASK_STATUS_CANCEL  );
        task.setDesc(desc.getDesc());
        boolean update = this.updateById(task);

        //取消工单后减少redis中人员工单的数量
        updateUserTaskRedis(task , -1);
        return update;
    }

    /**
     * 接受订单
     *
     * @param taskId
     * @return
     */
    @Override
    public boolean acceptTask(String taskId) {
        TaskEntity task = getById(Long.parseLong(taskId));
        if(task.getTaskStatus() != VMSystem.TASK_STATUS_CREATE){
            throw new LogicException("工单不是未处理状态");
        }
        task.setTaskStatus(VMSystem.TASK_STATUS_PROGRESS);
        boolean update = updateById(task);
        return update;
    }

    /**
     * 完成工单
     *
     * @param taskId
     * @return
     */
    @Override
    public boolean completeTask(String taskId) {
        TaskEntity task = getById(Long.parseLong(taskId));
        if (task.getTaskStatus() != VMSystem.TASK_STATUS_PROGRESS){
            throw new LogicException("工单不处于进行状态");
        }
        task.setTaskStatus(VMSystem.TASK_STATUS_FINISH);
        boolean update = updateById(task);


        //通过EMQ发送维修工单类型消息,到设备微服务,修改设备状态
        if (task.getProductTypeId() == VMSystem.TASK_TYPE_DEPLOY && task.getProductTypeId() == VMSystem.TASK_TYPE_REVOKE) {
            //如果是投放,撤机工单,则需要发送消息到设备微服务,修改设备状态
            vmStatusBySend(task);
            //如果是补货工单,则需要发送消息,到设备微服务
        } else if (task.getProductTypeId() == VMSystem.TASK_TYPE_SUPPLY) {
            //发送补货消息
            vmchannelBySend(task);
            }
        return update;
    }

    /**
     * 获取工单数最少的人
     * @param regionId
     * @param isRepair 是否是运维工单
     * @return
     */
    @Override
    public int getLeastUser(Long regionId, Boolean isRepair) {
        //假设是运营工单 则执行人是运营人员
        String roleCode = VMSystem.USER_OPERATOR;
        //如果是运维工单 则执行人是运维人员
        if(isRepair){
             roleCode = VMSystem.USER_REPAIRER;
        }
        //以固定字符串（前缀）+日期+区域+工单类别（运营/运维）为大key，以人员id做小key，过期时间为2天。
        String key = VMSystem.REGION_TASK_KEY_PREF
                + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))
                + regionId
                + roleCode;

        Set<Object> range = redisTemplate.opsForZSet().range(key, 0, 1);
        if (range == null){
            throw new LogicException("当前区域没有相关人员");
        }
        return (Integer) range.stream().collect(Collectors.toList()).get(0);
    }

    /**
     * 发送补货消息
     * @param task
     */
    private void vmchannelBySend(TaskEntity task) {
        //获取补货信息
        List<TaskDetailsEntity> list = taskDetailsService.list(Wrappers.<TaskDetailsEntity>lambdaQuery()
                .eq(TaskDetailsEntity::getTaskId, task.getTaskId()));


        SupplyContract contract = new SupplyContract();
        contract.setInnerCode(task.getInnerCode());

        //遍历补货信息,拷贝数据到 SupplyContract
        List<SupplyChannel> channels = new ArrayList<>();
        for (TaskDetailsEntity taskDetailsEntity : list) {
            SupplyChannel channel = new SupplyChannel();
            channel.setChannelId(taskDetailsEntity.getChannelCode());//货道编号
            channel.setCapacity(taskDetailsEntity.getExpectCapacity());//补货数量
            channels.add(channel);
        }
        contract.setSupplyData(channels);

        try {
            //发送消息
            mqttProducer.send(TopicConfig.VMS_SUPPLY_TOPIC, 2 ,contract);
        } catch (JsonProcessingException e) {
            throw new LogicException("补货工单消息发送错误 : " + e);
        }

    }

    /**
     * 通过EMQ发送维修工单类型消息,到设备微服务,修改设备状态
     * @param task
     * @throws JsonProcessingException
     */
    private void vmStatusBySend(TaskEntity task)  {


            //创建TaskCompleteContract对象,封装工单类型和设备编号
            TaskCompleteContract taskCompleteContract = new TaskCompleteContract();
            taskCompleteContract.setTaskType(task.getProductTypeId());
            taskCompleteContract.setInnerCode(task.getInnerCode());
            //通过EMQ发送消息
            try {
                mqttProducer.send(TopicConfig.VMS_COMPLETED_TOPIC , 2 , taskCompleteContract);
            } catch (JsonProcessingException e) {
                throw new LogicException("发送消息异常 : " + e);
            }

    }

    /**
     *设备状态判断
     * @param model
     * @param vmInfo
     */
    private void check(TaskViewModel model, VmVO vmInfo) {

        //查询设备是否存在
        if (vmInfo == null){
            throw new LogicException("设备不存在");
        }

            //售货机未投放时 , 不是投放工单
            if (vmInfo.getStatus() == VMSystem.VM_STATUS_NODEPLOY || model.getProductType() != VMSystem.TASK_TYPE_DEPLOY ){
                    throw new LogicException("该设备未投放");
            }

            //售货机在运营时 , 投放工单
            if (vmInfo.getStatus() == VMSystem.VM_STATUS_RUNNING || model.getProductType() == VMSystem.TASK_TYPE_DEPLOY ){
                    throw new LogicException("该设备已投放");
            }

            //售货机状态撤机
            if (vmInfo.getStatus() == VMSystem.VM_STATUS_REVOKE){
                throw new LogicException("该设备已撤机");
            }

    }

    /**
     * 用redis自增创建TaskCode
     * @return
     */
    private String createTaskCode() {
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String key = "lkd.task.code" + date;
        Object obj = redisTemplate.opsForValue().get(key);
        if (obj == null){
            redisTemplate.opsForValue().set(key, 1l , Duration.ofDays(1));
        }
        return date + String.format("%04d" , redisTemplate.opsForValue().increment(key,1));
    }
}
